#ifndef __WIZ_ALGEBRA_MATRIX44_TEMPLATE_HPP__SHANHAOBO_19800429__
#define __WIZ_ALGEBRA_MATRIX44_TEMPLATE_HPP__SHANHAOBO_19800429__

#include "./WizAlgebraMatrix44RowMajorMethod.hpp"
#include "./WizAlgebraMatrix44ColMajorMethod.hpp"

#include "../Matrix22/WizAlgebraMatrix22.hpp"
#include "../Matrix33/WizAlgebraMatrix33.hpp"
#include "../Vector2/WizAlgebraVector2.hpp"
#include "../Vector3/WizAlgebraVector3.hpp"

namespace Wiz
{
    namespace Matrix44
    {
        namespace Template
        {
            template<class ElementT, class DerivedT, class MethodT, class BaseT>
            struct Type : public BaseT
            {
                //////////////////////////////////////////////////////////
                typedef ElementT                            tElement;
                typedef BaseT                               tSuper;
                typedef MethodT                             tMethod;
                typedef DerivedT                            tDerived;

                typedef Type                                tThis;
                typedef tThis                               tCore;

                typedef ::Wiz::Vector2::Type<tElement>      tVector2;
                typedef ::Wiz::Vector3::Type<tElement>      tVector3;
                typedef ::Wiz::Vector4::Type<tElement>      tVector4;
                //////////////////////////////////////////////////////////////////////////
                WIZ_INLINE const tDerived& ToDerived() const
                {
                    return *((tDerived*)this);
                }
                WIZ_INLINE tDerived& ToDerived()
                {
                    return *((tDerived*)this);
                }
                ////////////////////////////////////////////////////////////////
                WIZ_INLINE tDerived& Transpose(tDerived& OutMat) const
                {
                    return tMethod::Transpose(OutMat, ToDerived());
                }
                WIZ_INLINE tDerived& Transpose()
                {
                    return tMethod::Transpose(ToDerived());
                }
                ////////////////////////////////////////////////////////////////
                WIZ_INLINE tDerived& Identity()
                {
                    return tMethod::Identity(ToDerived());
                }
                WIZ_INLINE tDerived& Zero()
                {
                    return tMethod::Zero(ToDerived());
                }
                ////////////////////////////////////////////////////////////////
                WIZ_INLINE tElement Determinant() const
                {
                    return tMethod::Determinant(ToDerived());
                }
                ////////////////////////////////////////////////////////////////
                ::Wiz::Bool::Type Invert(tDerived& OutMat) const
                {
                    return tMethod::Invert(OutMat, ToDerived());
                }
                ::Wiz::Bool::Type Invert()
                {
                    return tMethod::Invert(ToDerived())
                }
                //////////////////////////////////////////////////////////
                WIZ_INLINE friend tDerived operator+(const tDerived& InMat1, const tDerived& InMat2)
                {
                    tDerived OutMat;
                    return tMethod::Add(OutMat, InMat1, InMat2);
                }
                WIZ_INLINE friend tDerived& operator+=(tDerived& InOutMat, const tDerived& InMat)
                {
                    return tMethod::AddAssign(InOutMat, InMat);
                }

                WIZ_INLINE friend tDerived operator-(const tDerived& InMat1, const tDerived& InMat2)
                {
                    tDerived OutMat;
                    return tMethod::Subtract(OutMat, InMat1, InMat2);
                }
                WIZ_INLINE friend tDerived& operator-=(tDerived& InOutMat, const tDerived& InMat2)
                {
                    return tMethod::SubtractAssign(InOutMat, InMat2);
                }

                WIZ_INLINE friend tDerived operator*(const tDerived& InMat1, const tDerived& InMat2)
                {
                    tDerived Mat;
                    return tMethod::Multiply(Mat, InMat1, InMat2);
                }
                WIZ_INLINE friend tDerived& operator*=(tDerived& InOutMat, const tDerived& InMat)
                {
                    return tMethod::MultiplyAssign(InOutMat, InMat);
                }

                WIZ_INLINE friend tDerived operator*(const tDerived& InMat, const tElement& InEle)
                {
                    tDerived Mat;
                    return tMethod::Multiply(Mat, InMat, InEle);
                }
                WIZ_INLINE friend tDerived& operator*=(tDerived& InOutMat, const tElement& InEle)
                {
                    return tMethod::MultiplyAssign(InOutMat, InEle);
                }

                WIZ_INLINE friend tVector3 operator*(const tVector3& InVec3, const tDerived& InMat)
                {
                    tVector3 Vec3;
                    return tMethod::Multiply(Vec3, InVec3, InMat);
                }
                WIZ_INLINE friend tVector3& operator*=(tVector3& InOutVec3, const tDerived& InMat)
                {
                    return tMethod::MultiplyAssign(InOutVec3, InMat);
                }

                WIZ_INLINE friend tVector4 operator*(const tVector4& InVec4, const tDerived& InMat)
                {
                    tVector4 Vec4;
                    return tMethod::Multiply(Vec4, InVec4, InMat);
                }
                WIZ_INLINE friend tVector4& operator*=(tVector4& InOutVec4, const tDerived& InMat)
                {
                    return tMethod::MultiplyAssign(InOutVec4, InMat);
                }
                ////////////////////////////////////////////////////////////////

                ////////////////////////////////////////////////////////////////
                WIZ_INLINE ::Wiz::Bool::Type operator==(const tDerived& InMat) const
                {
                    return tMethod::IsEqual(*this, InMat);
                }
                WIZ_INLINE ::Wiz::Bool::Type operator!=(const tDerived& InMat) const
                {
                    return tMethod::IsEqual(*this, InMat) == ::Wiz::Bool::False;
                }
                WIZ_INLINE const tDerived& operator+() const
                {
                    return *this;
                }
                WIZ_INLINE tDerived operator-() const
                {
                    tDerived OutMat;
                    return tMethod::Negative(OutMat, *this);
                }
                ////////////////////////////////////////////////////////////////
                WIZ_INLINE tDerived& operator=(const tElement& InEle)
                {
                    return tMethod::FromElement(*this, InEle);
                }
                //////////////////////////////////////////////////////////////////////////
                WIZ_INLINE tDerived& operator=(const tDerived& InMat)
                {
                    return tMethod::FromMatrix44(*this, InMat);
                }

                WIZ_INLINE ::Wiz::Void::Type GetTransposeArray(float* OutArray) const
                {
                    OutArray[0] = this->e00;  OutArray[1] = this->e10;  OutArray[2] = this->e20;  OutArray[3] = this->e30;
                    OutArray[4] = this->e01;  OutArray[5] = this->e11;  OutArray[6] = this->e21;  OutArray[7] = this->e31;
                    OutArray[8] = this->e02;  OutArray[9] = this->e12;  OutArray[10] = this->e22; OutArray[11] = this->e32;
                    OutArray[12] = this->e03; OutArray[13] = this->e13; OutArray[14] = this->e23; OutArray[15] = this->e33;
                }
                ////////////////////////////////////////////////////////////////
            }; /// end of struct Type
        } /// end of namespace Template
    } /// end of namespace Matrix44
} /// end of namespace Wiz

#endif /*__WIZ_ALGEBRA_MATRIX44_TEMPLATE_HPP__SHANHAOBO_19800429__*/
